/*
 * WorkPingApiMain.cpp
 *
 *  Created on: 2016-6-21
 *      Author: root
 */
#include"WorkPingApi.h"
#include"WorkPriApi.h"
#ifndef NOUSE_PUBLIC_LOG
#include"PublicLog.h"
#endif
#if USE_PUBLIC_PING
#include"PublicNetHead.h"
#include<errno.h>
//一些时间显示
#define SHOW_USETIME 0
#ifdef __MINGW32__
#define USE_RECV_MSG 0
#else
#include<sys/ioctl.h>
#define USE_RECV_MSG 1
#endif
#if USE_PUBLIC_UDP
int CreateUDPSocket(int INTERVAL_TIME,const char*SRC_IP){
    int udp_sock=socket(AF_INET,SOCK_DGRAM,IPPROTO_UDP);
    if((SRC_IP!=NULL)&&(SRC_IP[0]!='\0')&&(strcmp(SRC_IP,"0.0.0.0")!=0)){
		struct sockaddr_in source={0};
		source.sin_family = AF_INET;
		source.sin_addr.s_addr=inet_addr(SRC_IP);
		if(bind(udp_sock,(struct sockaddr*)&source,sizeof(source))!=0){
			int errnum=errno;
			WRONG("绑定源IP:%s 失败 ERRNO:%d",SRC_IP,errnum);
        }
     }
    return udp_sock;
}
int CreateICMPSocketUDP(int INTERVAL_TIME){
    int    icmp_sock=socket(AF_INET,SOCK_RAW,IPPROTO_ICMP);
#ifndef __MINGW32__	
    uint32_t         flag=1<<ICMP_ECHO;
#ifndef ICMP_FILTER
#define ICMP_FILTER 1
#endif
//屏蔽ICMP请求
    if(setsockopt(icmp_sock,SOL_RAW,ICMP_FILTER,&flag,sizeof(uint32_t))!=0){
		WRONG("setsockopt SOL_RAW ICMP_FILTER err");
    }
#endif	
#ifdef __MINGW32__
	int tv=INTERVAL_TIME/1000;
	if(tv==0)tv=1;
#else	
    if(INTERVAL_TIME<10)INTERVAL_TIME=10;
    struct timeval  tv={0,INTERVAL_TIME/10};
#endif	
    if(setsockopt(icmp_sock,SOL_SOCKET,SO_RCVTIMEO,(char*)&tv,sizeof(tv))!=0){
		WRONG("setsockopt SO_RCVTIMEO err");
    }
#ifndef __MINGW32__		
    struct linger so_linger={1,0};
    if(setsockopt(icmp_sock,SOL_SOCKET,SO_LINGER,&so_linger,sizeof(struct linger))!=0){
		WRONG("setsockopt SO_LINGER err");
    }
#endif	
    return icmp_sock;
}
#endif
#if USE_PUBLIC_SOCKET
//0成功 -1失败
int SendICMPPacket(CPublicSocket*sock,int PacketSize,IPPINGCHECK&checkinfo,int ttl){
	char   Buffer[10240]={0};
	int sendsize=MakePingPacket(Buffer,PacketSize,checkinfo.seqinfo,checkinfo.ip,&checkinfo.sendtime);
	int RealSendize=sock->Send(Buffer,sendsize,checkinfo.ip,0,ttl);
	if(RealSendize!=sendsize){
		if(errno==9||errno==22)
			return -2;
		return -3;
	}
	return 0;
}
//-2 无关，-1其他错误，0 ttl，其他耗时
int ReadICMPPacket(CPublicSocket*sock,int checkid,IPPINGCHECK&checkinfo,struct timeval&timestamp,char*fromip){
	char recvpacket[10240]={0};
	int ipflag=0;
	int rc=sock->Recv(recvpacket,sizeof(recvpacket),fromip,&timestamp,&ipflag);
	if(rc<1){
//没有包接收	
		if(errno==9)
			return -3;
		return -11;
    }
/*	
	if(0){
		char filename[256]={0};
		snprintf(filename,255,"save_ipv%d_%d",ipflag,rand());
		FILE*fp=fopen(filename,"wb");
		fwrite(recvpacket,1,rc,fp);
		fclose(fp);
	}
*/
	if(ipflag==6){
		return ReadPingPacket6(recvpacket,rc,timestamp,checkid,&checkinfo.seqinfo,checkinfo.ip,fromip);
	}else{
		return ReadPingPacket(recvpacket,rc,timestamp,checkid,&checkinfo.seqinfo,checkinfo.ip,fromip);
	}
	return -11;
}
#endif
#if USE_PUBLIC_UDP
int ReadICMPUDPPacket(int icmp_sock,CONNECTKEY*key){
    char recvpacket[10240]={0};
    int  errnum=0;
#if USE_RECV_MSG	
    struct msghdr msg;
    struct sockaddr_in clientAddr;
    msg.msg_name = &clientAddr;
    msg.msg_namelen= sizeof(clientAddr);

    struct iovec iov;
    msg.msg_iov = &iov;
    msg.msg_iovlen =1;
    iov.iov_base= recvpacket;
    iov.iov_len = sizeof(recvpacket);

    char ctrl[CMSG_SPACE(sizeof(struct timeval))]={0};
    msg.msg_control = (caddr_t)ctrl;
    msg.msg_controllen = sizeof(ctrl);
    int rc = recvmsg(icmp_sock,&msg,0);
#else
	int rc=-1;
#endif	
    if(rc==-1){
		errnum=errno;
		if(errnum!=EAGAIN){
			static time_t checktime=0;
			time_t now=time(NULL);
//1s内禁止问题重复报告
			if(now>(checktime+2)){
				checktime=now;
				WRONG("接收失败 Error:%s(%d)\n",strerror(errnum),errnum);
            }
		}else{
			return -11;
        }
		return -2;
    }
    return ReadPingUDPPacket(recvpacket,rc,key);
}
#endif
//初始化PING风格
PINGFLAG InitPingFlag(){
    PINGFLAG flag={0};
    flag.CheckID=0;
    flag.PacketSize=80;//至少80 否则易出错
    flag.PerListTime=1000;
    flag.TimeOut=2000;
    flag.PerNum=40;
    flag.PerTime=3;
    flag.PingTimes=10;
	flag.bSupportIPV6=IsSupportIPV6();
    strcpy(flag.SrcIP,"0.0.0.0");
    return flag;
}
//显示PING风格
void ShowPingFlag(PINGFLAG flag,size_t num){
    INFO("数量:%zd 检测ID:%hhu 发送大小:%d 总次数:%d 超时:%d ms 发送间隔:%d ms 单次数量:%d 构建间隔:%d ms 源IP:%s IPV6支持:%d",
    num,flag.CheckID,flag.PacketSize,flag.PingTimes,flag.TimeOut,flag.PerTime,flag.PerNum,flag.PerListTime,flag.SrcIP,flag.bSupportIPV6);
}
#endif
